home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / cdrtools-1.10 / libhfs_iso / volume.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-06  |  14.4 KB  |  771 lines

  1. /*
  2.  * hfsutils - tools for reading and writing Macintosh HFS volumes
  3.  * Copyright (C) 1996, 1997 Robert Leslie
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20. #include <mconfig.h>
  21. #include <stdxlib.h>
  22. #include <strdefs.h>
  23. #include <errno.h>
  24. #include <time.h>
  25.  
  26. #include "internal.h"
  27. #include "data.h"
  28. #include "low.h"
  29. #include "btree.h"
  30. #include "record.h"
  31. #include "volume.h"
  32.  
  33. static    void    markexts    __PR((block *vbm, ExtDataRec *exts));
  34.  
  35.  
  36. /*
  37.  * NAME:    vol->catsearch()
  38.  * DESCRIPTION:    search catalog tree
  39.  */
  40. int v_catsearch(vol, parid, name, data, cname, np)
  41.     hfsvol        *vol;
  42.     long        parid;
  43.     char        *name;
  44.     CatDataRec    *data;
  45.     char        *cname;
  46.     node        *np;
  47. {
  48.   CatKeyRec key;
  49.   unsigned char pkey[HFS_CATKEYLEN];
  50.   node n;
  51.   unsigned char *ptr;
  52.   int found;
  53.  
  54.   if (np == 0)
  55.     np = &n;
  56.  
  57.   r_makecatkey(&key, parid, name);
  58.   r_packcatkey(&key, pkey, 0);
  59.  
  60.   found = bt_search(&vol->cat, pkey, np);
  61.   if (found <= 0)
  62.     return found;
  63.  
  64.   ptr = HFS_NODEREC(*np, np->rnum);
  65.  
  66.   if (cname)
  67.     {
  68.       r_unpackcatkey(ptr, &key);
  69.       strcpy(cname, key.ckrCName);
  70.     }
  71.  
  72.   if (data)
  73.     r_unpackcatdata(HFS_RECDATA(ptr), data);
  74.  
  75.   return 1;
  76. }
  77.  
  78. /*
  79.  * NAME:    vol->extsearch()
  80.  * DESCRIPTION:    search extents tree
  81.  */
  82. int v_extsearch(file, fabn, data, np)
  83.     hfsfile        *file;
  84.     unsigned int    fabn;
  85.     ExtDataRec    *data;
  86.     node        *np;
  87. {
  88.   ExtKeyRec key;
  89.   ExtDataRec extsave;
  90.   unsigned int fabnsave;
  91.   unsigned char pkey[HFS_EXTKEYLEN];
  92.   node n;
  93.   unsigned char *ptr;
  94.   int found;
  95.  
  96.   if (np == 0)
  97.     np = &n;
  98.  
  99.   r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
  100.   r_packextkey(&key, pkey, 0);
  101.  
  102.   /* in case bt_search() clobbers these */
  103.  
  104.   memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
  105.   fabnsave = file->fabn;
  106.  
  107.   found = bt_search(&file->vol->ext, pkey, np);
  108.  
  109.   memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
  110.   file->fabn = fabnsave;
  111.  
  112.   if (found <= 0)
  113.     return found;
  114.  
  115.   if (data)
  116.     {
  117.       ptr = HFS_NODEREC(*np, np->rnum);
  118.       r_unpackextdata(HFS_RECDATA(ptr), data);
  119.     }
  120.  
  121.   return 1;
  122. }
  123.  
  124. /*
  125.  * NAME:    vol->getthread()
  126.  * DESCRIPTION:    retrieve catalog thread information for a file or directory
  127.  */
  128. int v_getthread(vol, id, thread, np, type)
  129.     hfsvol        *vol;
  130.     long        id;
  131.     CatDataRec    *thread;
  132.     node        *np;
  133.     int        type;
  134. {
  135.   CatDataRec rec;
  136.   int found;
  137.  
  138.   if (thread == 0)
  139.     thread = &rec;
  140.  
  141.   found = v_catsearch(vol, id, "", thread, 0, np);
  142.   if (found <= 0)
  143.     return found;
  144.  
  145.   if (thread->cdrType != type)
  146.     {
  147.       ERROR(EIO, "bad thread record");
  148.       return -1;
  149.     }
  150.  
  151.   return 1;
  152. }
  153.  
  154. /*
  155.  * NAME:    vol->putcatrec()
  156.  * DESCRIPTION:    store catalog information
  157.  */
  158. int v_putcatrec(data, np)
  159.     CatDataRec    *data;
  160.     node        *np;
  161. {
  162.   unsigned char pdata[HFS_CATDATALEN], *ptr;
  163.   int len = 0;
  164.  
  165.   r_packcatdata(data, pdata, &len);
  166.  
  167.   ptr = HFS_NODEREC(*np, np->rnum);
  168.   memcpy(HFS_RECDATA(ptr), pdata, len);
  169.  
  170.   return bt_putnode(np);
  171. }
  172.  
  173. /*
  174.  * NAME:    vol->putextrec()
  175.  * DESCRIPTION:    store extent information
  176.  */
  177. int v_putextrec(data, np)
  178.     ExtDataRec    *data;
  179.     node        *np;
  180. {
  181.   unsigned char pdata[HFS_EXTDATALEN], *ptr;
  182.   int len = 0;
  183.  
  184.   r_packextdata(data, pdata, &len);
  185.  
  186.   ptr = HFS_NODEREC(*np, np->rnum);
  187.   memcpy(HFS_RECDATA(ptr), pdata, len);
  188.  
  189.   return bt_putnode(np);
  190. }
  191.  
  192. /*
  193.  * NAME:    vol->allocblocks()
  194.  * DESCRIPTION:    allocate a contiguous range of blocks
  195.  */
  196. int v_allocblocks(vol, blocks)
  197.     hfsvol        *vol;
  198.     ExtDescriptor    *blocks;
  199. {
  200.   unsigned int request, found, foundat, start, end, pt;
  201.   block *vbm;
  202.   int wrap = 0;
  203.  
  204.   if (vol->mdb.drFreeBks == 0)
  205.     {
  206.       ERROR(ENOSPC, "volume full");
  207.       return -1;
  208.     }
  209.  
  210.   request = blocks->xdrNumABlks;
  211.   found   = 0;
  212.   foundat = 0;
  213.   start   = vol->mdb.drAllocPtr;
  214.   end     = vol->mdb.drNmAlBlks;
  215.   pt      = start;
  216.   vbm     = vol->vbm;
  217.  
  218.   if (request == 0)
  219.     abort();
  220.  
  221.   while (1)
  222.     {
  223.       unsigned int mark;
  224.  
  225.       /* skip blocks in use */
  226.  
  227.       while (pt < end && BMTST(vbm, pt))
  228.     ++pt;
  229.  
  230.       if (wrap && pt >= start)
  231.     break;
  232.  
  233.       /* count blocks not in use */
  234.  
  235.       mark = pt;
  236.       while (pt < end && pt - mark < request && ! BMTST(vbm, pt))
  237.     ++pt;
  238.  
  239.       if (pt - mark > found)
  240.     {
  241.       found   = pt - mark;
  242.       foundat = mark;
  243.     }
  244.  
  245.       if (pt == end)
  246.     pt = 0, wrap = 1;
  247.  
  248.       if (found == request)
  249.     break;
  250.     }
  251.  
  252.   if (found == 0 || found > vol->mdb.drFreeBks)
  253.     {
  254.       ERROR(EIO, "bad volume bitmap or free block count");
  255.       return -1;
  256.     }
  257.  
  258.   blocks->xdrStABN    = foundat;
  259.   blocks->xdrNumABlks = found;
  260.  
  261.   vol->mdb.drAllocPtr = pt;
  262.   vol->mdb.drFreeBks -= found;
  263.  
  264.   for (pt = foundat; pt < foundat + found; ++pt)
  265.     BMSET(vbm, pt);
  266.  
  267.   vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
  268.  
  269.   return 0;
  270. }
  271.  
  272. /*
  273.  * NAME:    vol->freeblocks()
  274.  * DESCRIPTION:    deallocate a contiguous range of blocks
  275.  */
  276. void v_freeblocks(vol, blocks)
  277.     hfsvol        *vol;
  278.     ExtDescriptor    *blocks;
  279. {
  280.   unsigned int start, len, pt;
  281.   block *vbm;
  282.  
  283.   start = blocks->xdrStABN;
  284.   len   = blocks->xdrNumABlks;
  285.   vbm   = vol->vbm;
  286.  
  287.   vol->mdb.drFreeBks += len;
  288.  
  289.   for (pt = start; pt < start + len; ++pt)
  290.     BMCLR(vbm, pt);
  291.  
  292.   vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
  293. }
  294.  
  295. /*
  296.  * NAME:    vol->resolve()
  297.  * DESCRIPTION:    translate a pathname; return catalog information
  298.  */
  299. int v_resolve(vol, path, data, parid, fname, np)
  300.     hfsvol        **vol;
  301.     char        *path;
  302.     CatDataRec    *data;
  303.     long        *parid;
  304.     char        *fname;
  305.     node        *np;
  306. {
  307.   long dirid;
  308.   char name[HFS_MAX_FLEN + 1], *nptr;
  309.   int found;
  310.  
  311.   if (*path == 0)
  312.     {
  313.       ERROR(ENOENT, "empty path");
  314.       return -1;
  315.     }
  316.  
  317.   if (parid)
  318.     *parid = 0;
  319.  
  320.   nptr = strchr(path, ':');
  321.  
  322.   if (*path == ':' || nptr == 0)
  323.     {
  324.       dirid = (*vol)->cwd;  /* relative path */
  325.  
  326.       if (*path == ':')
  327.     ++path;
  328.  
  329.       if (*path == 0)
  330.     {
  331.       found = v_getdthread(*vol, dirid, data, 0);
  332.       if (found <= 0)
  333.         return found;
  334.  
  335.       if (parid)
  336.         *parid = data->u.dthd.thdParID;
  337.  
  338.       return v_catsearch(*vol, data->u.dthd.thdParID,
  339.                  data->u.dthd.thdCName, data, fname, np);
  340.     }
  341.     }
  342.   else
  343.     {
  344.       hfsvol *check;
  345.  
  346.       dirid = HFS_CNID_ROOTPAR;  /* absolute path */
  347.  
  348.       if (nptr - path > HFS_MAX_VLEN)
  349.     {
  350.       ERROR(ENAMETOOLONG, 0);
  351.       return -1;
  352.     }
  353.  
  354.       strncpy(name, path, nptr - path);
  355.       name[nptr - path] = 0;
  356.  
  357.       for (check = hfs_mounts; check; check = check->next)
  358.     {
  359.       if (d_relstring(check->mdb.drVN, name) == 0)
  360.         {
  361.           *vol = check;
  362.           break;
  363.         }
  364.     }
  365.     }
  366.  
  367.   while (1)
  368.     {
  369.       while (*path == ':')
  370.     {
  371.       ++path;
  372.  
  373.       found = v_getdthread(*vol, dirid, data, 0);
  374.       if (found <= 0)
  375.         return found;
  376.  
  377.       dirid = data->u.dthd.thdParID;
  378.     }
  379.  
  380.       if (*path == 0)
  381.     {
  382.       found = v_getdthread(*vol, dirid, data, 0);
  383.       if (found <= 0)
  384.         return found;
  385.  
  386.       if (parid)
  387.         *parid = data->u.dthd.thdParID;
  388.  
  389.       return v_catsearch(*vol, data->u.dthd.thdParID,
  390.                  data->u.dthd.thdCName, data, fname, np);
  391.     }
  392.  
  393.       nptr = name;
  394.       while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
  395.     *nptr++ = *path++;
  396.  
  397.       if (*path && *path != ':')
  398.     {
  399.       ERROR(ENAMETOOLONG, 0);
  400.       return -1;
  401.     }
  402.  
  403.       *nptr = 0;
  404.       if (*path == ':')
  405.     ++path;
  406.  
  407.       if (parid)
  408.     *parid = dirid;
  409.  
  410.       found = v_catsearch(*vol, dirid, name, data, fname, np);
  411.       if (found < 0)
  412.     return -1;
  413.  
  414.       if (found == 0)
  415.     {
  416.       if (*path && parid)
  417.         *parid = 0;
  418.  
  419.       if (*path == 0 && fname)
  420.         strcpy(fname, name);
  421.  
  422.       return 0;
  423.     }
  424.  
  425.       switch (data->cdrType)
  426.     {
  427.     case cdrDirRec:
  428.       if (*path == 0)
  429.         return 1;
  430.  
  431.       dirid = data->u.dir.dirDirID;
  432.       break;
  433.  
  434.     case cdrFilRec:
  435.       if (*path == 0)
  436.         return 1;
  437.  
  438.       ERROR(ENOTDIR, "invalid pathname");
  439.       return -1;
  440.  
  441.     default:
  442.       ERROR(EIO, "unexpected catalog record");
  443.       return -1;
  444.     }
  445.     }
  446. }
  447.  
  448. /*
  449.  * NAME:    vol->destruct()
  450.  * DESCRIPTION:    free memory consumed by a volume descriptor
  451.  */
  452. void v_destruct(vol)
  453.     hfsvol    *vol;
  454. {
  455.   FREE(vol->vbm);
  456.  
  457.   FREE(vol->ext.map);
  458.   FREE(vol->cat.map);
  459.  
  460.   FREE(vol);
  461. }
  462.  
  463. /*
  464.  * NAME:    vol->getvol()
  465.  * DESCRIPTION:    validate a volume reference
  466.  */
  467. int v_getvol(vol)
  468.     hfsvol    **vol;
  469. {
  470.   if (*vol == 0)
  471.     {
  472.       if (hfs_curvol == 0)
  473.     {
  474.       ERROR(EINVAL, "no volume is current");
  475.       return -1;
  476.     }
  477.  
  478.       *vol = hfs_curvol;
  479.     }
  480.  
  481.   return 0;
  482. }
  483.  
  484. /*
  485.  * NAME:    vol->flush()
  486.  * DESCRIPTION:    flush all pending changes (B*-tree, MDB, VBM) to disk
  487.  */
  488. int v_flush(vol, umounting)
  489.     hfsvol    *vol;
  490.     int    umounting;
  491. {
  492.   if (! (vol->flags & HFS_READONLY))
  493.     {
  494.       if ((vol->ext.flags & HFS_UPDATE_BTHDR) &&
  495.       bt_writehdr(&vol->ext) < 0)
  496.     return -1;
  497.  
  498.       if ((vol->cat.flags & HFS_UPDATE_BTHDR) &&
  499.       bt_writehdr(&vol->cat) < 0)
  500.     return -1;
  501.  
  502.       if ((vol->flags & HFS_UPDATE_VBM) &&
  503.       l_writevbm(vol) < 0)
  504.     return -1;
  505.  
  506.       if (umounting &&
  507.       ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
  508.     {
  509.       vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED;
  510.       vol->flags |= HFS_UPDATE_MDB;
  511.     }
  512.  
  513.       if ((vol->flags & (HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB)) &&
  514.       l_writemdb(vol) < 0)
  515.     return -1;
  516.     }
  517.  
  518.   return 0;
  519. }
  520.  
  521. /*
  522.  * NAME:    vol->adjvalence()
  523.  * DESCRIPTION:    update a volume's valence counts
  524.  */
  525. int v_adjvalence(vol, parid, isdir, adj)
  526.     hfsvol    *vol;
  527.     long    parid;
  528.     int    isdir;
  529.     int    adj;
  530. {
  531.   node n;
  532.   CatDataRec data;
  533.  
  534.   if (isdir)
  535.     vol->mdb.drDirCnt += adj;
  536.   else
  537.     vol->mdb.drFilCnt += adj;
  538.  
  539.   vol->flags |= HFS_UPDATE_MDB;
  540.  
  541.   if (parid == HFS_CNID_ROOTDIR)
  542.     {
  543.       if (isdir)
  544.     vol->mdb.drNmRtDirs += adj;
  545.       else
  546.     vol->mdb.drNmFls    += adj;
  547.     }
  548.   else if (parid == HFS_CNID_ROOTPAR)
  549.     return 0;
  550.  
  551.   if (v_getdthread(vol, parid, &data, 0) <= 0 ||
  552.       v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName,
  553.           &data, 0, &n) <= 0 ||
  554.       data.cdrType != cdrDirRec)
  555.     {
  556.       ERROR(EIO, "can't find parent directory");
  557.       return -1;
  558.     }
  559.  
  560.   data.u.dir.dirVal  += adj;
  561.   data.u.dir.dirMdDat = d_tomtime(time(0));
  562.  
  563.   return v_putcatrec(&data, &n);
  564. }
  565.  
  566. /*
  567.  * NAME:    vol->newfolder()
  568.  * DESCRIPTION:    create a new HFS folder
  569.  */
  570. int v_newfolder(vol, parid, name)
  571.     hfsvol    *vol;
  572.     long    parid;
  573.     char    *name;
  574. {
  575.   CatKeyRec key;
  576.   CatDataRec data;
  577.   long id;
  578.   unsigned char record[HFS_CATRECMAXLEN];
  579.   int i, reclen;
  580.  
  581.   if (bt_space(&vol->cat, 2) < 0)
  582.     return -1;
  583.  
  584.   id = vol->mdb.drNxtCNID++;
  585.   vol->flags |= HFS_UPDATE_MDB;
  586.  
  587.   /* create directory record */
  588.  
  589.   data.cdrType   = cdrDirRec;
  590.   data.cdrResrv2 = 0;
  591.  
  592.   data.u.dir.dirFlags = 0;
  593.   data.u.dir.dirVal   = 0;
  594.   data.u.dir.dirDirID = id;
  595.   data.u.dir.dirCrDat = d_tomtime(time(0));
  596.   data.u.dir.dirMdDat = data.u.dir.dirCrDat;
  597.   data.u.dir.dirBkDat = 0;
  598.  
  599.   memset(&data.u.dir.dirUsrInfo,  0, sizeof(data.u.dir.dirUsrInfo));
  600.   memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo));
  601.   for (i = 0; i < 4; ++i)
  602.     data.u.dir.dirResrv[i] = 0;
  603.  
  604.   r_makecatkey(&key, parid, name);
  605.   r_packcatkey(&key, record, &reclen);
  606.   r_packcatdata(&data, HFS_RECDATA(record), &reclen);
  607.  
  608.   if (bt_insert(&vol->cat, record, reclen) < 0)
  609.     return -1;
  610.  
  611.   /* create thread record */
  612.  
  613.   data.cdrType   = cdrThdRec;
  614.   data.cdrResrv2 = 0;
  615.  
  616.   data.u.dthd.thdResrv[0] = 0;
  617.   data.u.dthd.thdResrv[1] = 0;
  618.   data.u.dthd.thdParID    = parid;
  619.   strcpy(data.u.dthd.thdCName, name);
  620.  
  621.   r_makecatkey(&key, id, "");
  622.   r_packcatkey(&key, record, &reclen);
  623.   r_packcatdata(&data, HFS_RECDATA(record), &reclen);
  624.  
  625.   if (bt_insert(&vol->cat, record, reclen) < 0 ||
  626.       v_adjvalence(vol, parid, 1, 1) < 0)
  627.     return -1;
  628.  
  629.   return 0;
  630. }
  631.  
  632. /*
  633.  * NAME:    markexts()
  634.  * DESCRIPTION:    set bits from an extent record in the volume bitmap
  635.  */
  636. static
  637. void markexts(vbm, exts)
  638.     block        *vbm;
  639.     ExtDataRec    *exts;
  640. {
  641.   int i;
  642.   unsigned int start, len;
  643.  
  644.   for (i = 0; i < 3; ++i)
  645.     {
  646.       for (start = (*exts)[i].xdrStABN,
  647.          len = (*exts)[i].xdrNumABlks; len--; ++start)
  648.     BMSET(vbm, start);
  649.     }
  650. }
  651.  
  652. /*
  653.  * NAME:    vol->scavenge()
  654.  * DESCRIPTION:    safeguard blocks in the volume bitmap
  655.  */
  656. int v_scavenge(vol)
  657.     hfsvol    *vol;
  658. {
  659.   block *vbm = vol->vbm;
  660.   node n;
  661.   unsigned int pt, blks;
  662.  
  663.   if (vbm == 0)
  664.     return 0;
  665.  
  666.   markexts(vbm, &vol->mdb.drXTExtRec);
  667.   markexts(vbm, &vol->mdb.drCTExtRec);
  668.  
  669.   vol->flags |= HFS_UPDATE_VBM;
  670.  
  671.   /* scavenge the extents overflow file */
  672.  
  673.   n.bt   = &vol->ext;
  674.   n.nnum = vol->ext.hdr.bthFNode;
  675.  
  676.   if (n.nnum > 0)
  677.     {
  678.       if (bt_getnode(&n) < 0)
  679.     return -1;
  680.  
  681.       n.rnum = 0;
  682.  
  683.       while (1)
  684.     {
  685.       ExtDataRec data;
  686.       unsigned char *ptr;
  687.  
  688.       while (n.rnum >= n.nd.ndNRecs)
  689.         {
  690.           n.nnum = n.nd.ndFLink;
  691.           if (n.nnum == 0)
  692.         break;
  693.  
  694.           if (bt_getnode(&n) < 0)
  695.         return -1;
  696.  
  697.           n.rnum = 0;
  698.         }
  699.  
  700.       if (n.nnum == 0)
  701.         break;
  702.  
  703.       ptr = HFS_NODEREC(n, n.rnum);
  704.       r_unpackextdata(HFS_RECDATA(ptr), &data);
  705.  
  706.       markexts(vbm, &data);
  707.  
  708.       ++n.rnum;
  709.     }
  710.     }
  711.  
  712.   /* scavenge the catalog file */
  713.  
  714.   n.bt   = &vol->cat;
  715.   n.nnum = vol->cat.hdr.bthFNode;
  716.  
  717.   if (n.nnum > 0)
  718.     {
  719.       if (bt_getnode(&n) < 0)
  720.     return -1;
  721.  
  722.       n.rnum = 0;
  723.  
  724.       while (1)
  725.     {
  726.       CatDataRec data;
  727.       unsigned char *ptr;
  728.  
  729.       while (n.rnum >= n.nd.ndNRecs)
  730.         {
  731.           n.nnum = n.nd.ndFLink;
  732.           if (n.nnum == 0)
  733.         break;
  734.  
  735.           if (bt_getnode(&n) < 0)
  736.         return -1;
  737.  
  738.           n.rnum = 0;
  739.         }
  740.  
  741.       if (n.nnum == 0)
  742.         break;
  743.  
  744.       ptr = HFS_NODEREC(n, n.rnum);
  745.       r_unpackcatdata(HFS_RECDATA(ptr), &data);
  746.  
  747.       if (data.cdrType == cdrFilRec)
  748.         {
  749.           markexts(vbm, &data.u.fil.filExtRec);
  750.           markexts(vbm, &data.u.fil.filRExtRec);
  751.         }
  752.  
  753.       ++n.rnum;
  754.     }
  755.     }
  756.  
  757.   for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; )
  758.     {
  759.       if (! BMTST(vbm, pt))
  760.     ++blks;
  761.     }
  762.  
  763.   if (vol->mdb.drFreeBks != blks)
  764.     {
  765.       vol->mdb.drFreeBks = blks;
  766.       vol->flags |= HFS_UPDATE_MDB;
  767.     }
  768.  
  769.   return 0;
  770. }
  771.